/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

// Lock test and set stores a value iff *ptr is 0

void
hw_sync_lock_release(volatile int *ptr)
{
  __asm__ volatile ("mov r0, %[ptr]\n"      // Pointer variable into r0
                    "mov r1, #0\n"
                    "str r1, [r0]\n"
                    : // No outs
                    : [ptr] "r" (ptr)
                    : "r0", "r1" // Clobber
                   );
}

#ifdef CORTEX_A8

 /* This is a hack for the OMAP / Cortex-A8. It avoids using the ldrex / strex
    instructions for aquiring the spinlock. This function is not safe to use on
    multi CPU / core systems, but will be fine on the beagle board as it
    protects the ptr updates by disabling interrupts temporarily.
 */

#include <interrupt.h>
#include "cpu.h"

int
hw_sync_lock_acquire(volatile int *ptr, int val)
{
  // TODO: Should vptr really point at a volatile here?

  volatile int *vptr = ptr;

  int result = 0;

  result = *vptr;
  // No point in disabling interrupts if the result is not valid
  if (result == 0) {
    uint32_t flags = save_flags();

    disable_interrupts();
    disable_fast_interrupts();

    result = *vptr;

    if (result == 0) {
      *vptr = val;
    }

    restore_flags(flags);

    dsb();
  }

  return result;
}



#else

int
hw_sync_lock_acquire(int *ptr, int val)
{
  int result = 0;

  // TODO: For multiprocessor systems, we need some memory barrier here
  // NOTE: Exclusive is cleared with the clrex in the context switch
  __asm__ volatile ("    mov r0,  %[ptr]\n"      // Pointer variable into r0
                    "    mov r1, %[val]\n"       // Value variable into r1
                    "1:  ldrex r2, [r0]\n"       // Exclusive load of r0,
                    "    strex r3, r1, [r0]\n"   // Storing val, r3 will be 0 if we are exclusive and 1 if not
                                                 // also, the store only have effect if we are exclusive
                    "    cmp r3, #0\n"         // Did the exclusive store succeed
                    "    bne 1b\n"               // If not, retry load and store
                    "    mov %[res], r2\n"       // Result should be the previous value of *ptr
                    : [res] "=r" (result) // Outs
                    : [ptr] "r" (ptr), [val] "r" (val) // Ins
                    : "r0", "r1", "r2", "r3" // Clobber
                    );

  return result;
}

#endif
